package org.devocean.groupbuying.service.impl;

import java.awt.Graphics2D;
import java.awt.Image;
import java.awt.RenderingHints;
import java.awt.image.BufferedImage;
import java.io.BufferedInputStream;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.sql.Timestamp;
import java.util.Calendar;
import java.util.List;
import java.util.Map;

import javax.imageio.ImageIO;

import org.devocean.groupbuying.dao.MediaDAO;
import org.devocean.groupbuying.domain.Media;
import org.devocean.groupbuying.service.MediaService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartFile;

import com.sun.image.codec.jpeg.JPEGCodec;
import com.sun.image.codec.jpeg.JPEGEncodeParam;
import com.sun.image.codec.jpeg.JPEGImageEncoder;

@Service
public class MediaServiceImpl implements MediaService {

	@Autowired
	private MediaDAO mediaDAO;

	@Value("#{mediaSettings.mediaPathResource}")
	private String mediaPathResource;

	@Value("#{mediaSettings.imageProperties}")
	private List<Map<String, String>> imageProperties;

	@Transactional(readOnly = true)
	public List<Media> findMediaEntries(int firstResult, int maxResults) {
		return mediaDAO.findMediaEntries(firstResult, maxResults);
	}

	@Transactional(readOnly = true)
	public long countMedia() {
		return mediaDAO.countMedia();
	}

	@Transactional
	public Boolean deleteMedia(final int id) {
		final Media media = mediaDAO.findMediaById(id);
		for (Map<String, String> map : imageProperties) {
			deleteScaledImage(mediaPathResource
					+ map.get("scale") + "\\" + media.getTitle());
		}

		return mediaDAO.delete(media);

	}

	@Transactional
	public Media saveMedia(final Media media) {
		final MultipartFile multipartFile = media.getFileData();
		final String strSize = multipartFile.getSize() / 1024 + "Ko";

		media.setMediaFileSize(strSize);
		media.setMediaFileSize(multipartFile.getSize() / 1024 + "Ko");
		media.setMediaFilePath(renameImage(multipartFile.getOriginalFilename()));
		media.setMediaContentType(multipartFile.getContentType());
		Media resultMedia = null;
		if (multipartFile.getSize() > 0) {

			for (Map<String, String> map : imageProperties) {
				createFolder(mediaPathResource + map.get("scale"));
				saveScaledImage(multipartFile, media.getMediaFilePath(),
						Integer.parseInt((String) map.get("width")),
						Integer.parseInt((String) map.get("height")),
						mediaPathResource + map.get("scale") + "\\");
			}

			resultMedia = mediaDAO.save(media);

		}

		

		return resultMedia;

	}

	/**
	 * Scale and save image in folder
	 * 
	 * @param multipartFile
	 * @param withwith
	 * @param height
	 * @param destination
	 */
	public void saveScaledImage(MultipartFile multipartFile, String fileName,
			int with, int height, String destination) {
		InputStream inputStream = null;
		OutputStream outputStream = null;
		try {
			inputStream = scaleImage(multipartFile.getInputStream(), with,
					height);
			outputStream = new FileOutputStream(destination + fileName);
			inputStream.close();
			int readBytes = 0;
			byte[] buffer = new byte[8192];
			while ((readBytes = inputStream.read(buffer, 0, 8192)) != -1) {
				outputStream.write(buffer, 0, readBytes);
			}
			outputStream.close();

		} catch (IOException e) {
			e.printStackTrace();
		} catch (Exception e) {
			e.printStackTrace();
		}

	}

	/**
	 * Delete file from folder
	 * 
	 * @param filePath
	 * @return Boolean
	 */
	public Boolean deleteScaledImage(String filePath) {
		try {

			File file = new File(filePath);
			return file.delete();

		} catch (Exception e) {
			e.printStackTrace();
			return false;

		}
	}

	/**
	 * create folder if it dosen't exists
	 * 
	 * @param folderPath
	 * @return Boolean
	 */
	public Boolean createFolder(String folderPath) {
		try {

			File file = new File(folderPath);
			boolean exists = file.exists();
			if (!exists) {
				file.mkdir();
				return true;
			} else
				return true;

		} catch (Exception e) {
			e.printStackTrace();
			return false;

		}
	}

	/**
	 * add to fileName the number of milliseconds since January 1, 1970,
	 * 00:00:00 GMT
	 * 
	 * @param fileName
	 * @return
	 */
	public String renameImage(String fileName) {
		Calendar calendar = Calendar.getInstance();
		Timestamp currentTimestamp = new java.sql.Timestamp(calendar.getTime()
				.getTime());
		int mid = fileName.lastIndexOf(".");
		String fname = fileName.substring(0, mid);
		String ext = fileName.substring(mid + 1, fileName.length());
		return fname + currentTimestamp.getTime() + "." + ext;
	}

	public InputStream scaleImage(InputStream p_image, int p_width, int p_height)
			throws Exception {

		InputStream imageStream = new BufferedInputStream(p_image);
		Image image = (Image) ImageIO.read(imageStream);

		int thumbWidth = p_width;
		int thumbHeight = p_height;

		// Make sure the aspect ratio is maintained, so the image is not skewed
		double thumbRatio = (double) thumbWidth / (double) thumbHeight;
		int imageWidth = image.getWidth(null);
		int imageHeight = image.getHeight(null);
		double imageRatio = (double) imageWidth / (double) imageHeight;
		if (thumbRatio < imageRatio) {
			thumbHeight = (int) (thumbWidth / imageRatio);
		} else {
			thumbWidth = (int) (thumbHeight * imageRatio);
		}

		// Draw the scaled image
		BufferedImage thumbImage = new BufferedImage(thumbWidth, thumbHeight,
				BufferedImage.TYPE_INT_RGB);
		Graphics2D graphics2D = thumbImage.createGraphics();
		graphics2D.setRenderingHint(RenderingHints.KEY_INTERPOLATION,
				RenderingHints.VALUE_INTERPOLATION_BILINEAR);
		graphics2D.drawImage(image, 0, 0, thumbWidth, thumbHeight, null);

		// Write the scaled image to the outputstream
		ByteArrayOutputStream out = new ByteArrayOutputStream();
		JPEGImageEncoder encoder = JPEGCodec.createJPEGEncoder(out);
		JPEGEncodeParam param = encoder.getDefaultJPEGEncodeParam(thumbImage);
		int quality = 100; // Use between 1 and 100, with 100 being highest
							// quality
		quality = Math.max(0, Math.min(quality, 100));
		param.setQuality((float) quality / 100.0f, false);
		encoder.setJPEGEncodeParam(param);
		encoder.encode(thumbImage);
		ImageIO.write(thumbImage, "image/jpeg", out);

		// Read the outputstream into the inputstream for the return value
		ByteArrayInputStream bis = new ByteArrayInputStream(out.toByteArray());

		return bis;
	}

	@Transactional(readOnly = true)
	public Media findMediaById(Integer id) {
		return mediaDAO.findMediaById(id);
	}

	@Transactional
	public Media updateMedia(Media media) {
		return mediaDAO.update(media);
	}

	public List<Media> search(String searchText) {
		return mediaDAO.findMediaByTitle(searchText);
	}

}